#ifndef VECTOR_H_
#define VECTOR_H_

#include <cmath>
#include <cassert>
#include <iostream>


template<typename T>
struct TVector2
{
    TVector2() :
        x(0), y(0)
    {}

    TVector2(T s) :
        x(s), y(s)
    {}

    TVector2(T x, T y) :
        x(x), y(y)
    {}

    TVector2(T vect[2]) :
        x(vect[0]), y(vect[1])
    {}

    inline T& operator[]( int i)
    { return (&x)[i]; }

    inline T operator[]( int i) const
    { return (&x)[i]; }

    inline TVector2 operator*(const T val) const
    { return TVector2(x*val, y*val); }

    inline TVector2 operator+(const TVector2& vec) const
    { return TVector2(x+vec.x, y+vec.y); }

    inline TVector2 operator-(const TVector2& vec) const
    { return TVector2(x-vec.x, y-vec.y); }

    inline TVector2 operator-() const
    { return TVector2(-x, -y); }

    inline TVector2 operator/(T val) const
    {
        assert(val != 0.0);
        val = 1.0/val;
        return TVector2(x*val, y*val);
    }

    inline T length() const
    { return sqrt(x*x + y*y); }

    inline T lengthSq() const
    { return x*x + y*y; }

    TVector2 normalized() const
    {
        T d = length();
        assert(d != 0.0);
        d = 1.0/d;
        return TVector2(x*d, y*d);
    }

    TVector2& normalize()
    {
        T d = length();
        assert(d != 0.0);
        d = 1.0/d;
        x *= d;
        y *= d;
        return *this;
    }

    T x, y;
};

template<typename T>
inline T crossProduct(const TVector2<T>& v1, const TVector2<T>& v2)
{ return v1.x * v2.y - v1.y * v2.x; }

template<typename T>
inline T dotProduct(const TVector2<T>& v1, const TVector2<T>& v2)
{ return v1.x * v2.x + v1.y * v2.y; }

template<typename T>
inline std::ostream& operator<<(std::ostream& output, const TVector2<T> &v)
{
    output << "[" <<  v.x << ", " << v.y << "]";
    return output;
}


template<typename T>
struct TVector3
{
    TVector3() :
        x(0), y(0), z(0)
    {}

    TVector3(T s) :
        x(s), y(s), z(s)
    {}

    TVector3(T x, T y, T z) :
        x(x), y(y), z(z)
    {}

    TVector3(T vect[3]) :
        x(vect[0]), y(vect[1]), z(vect[2])
    {}

    template<typename V3>
    explicit TVector3(const V3& v) :
        x(v.x), y(v.y), z(v.z)
    {}

    inline T& operator[]( int i)
    { return (&x)[i]; }

    inline T operator[]( int i) const
    { return (&x)[i]; }

    inline TVector3 operator*(const T val) const
    { return TVector3(x*val, y*val, z*val); }

    inline TVector3 operator+(const TVector3& vec) const
    { return TVector3(x+vec.x, y+vec.y, z+vec.z); }

    inline TVector3 operator-(const TVector3& vec) const
    { return TVector3(x-vec.x, y-vec.y, z-vec.z); }

    inline TVector3 operator-() const
    { return TVector3(-x, -y, -z); }

    inline TVector3 operator/(T val) const
    {
        assert(val != 0.0);
        val = 1.0/val;
        return TVector3(x*val, y*val, z*val);
    }

    inline T length() const
    { return sqrt(x*x + y*y + z*z); }

    inline T lengthSq() const
    { return x*x + y*y + z*z; }

    TVector3 normalized() const
    {
        T d = length();
        assert(d != 0.0);
        d = 1.0/d;
        return TVector3(x*d, y*d, z*d);
    }

    TVector3& normalize()
    {
        T d = length();
        assert(d != 0.0);
        d = 1.0/d;
        x *= d;
        y *= d;
        z *= d;
        return *this;
    }

    T x, y, z;
};

template<typename T>
inline TVector3<T> crossProduct(const TVector3<T>& v1, const TVector3<T>& v2)
{ return TVector3<T>(v1.y * v2.z - v1.z * v2.y, v1.z * v2.x - v1.x * v2.z, v1.x * v2.y - v1.y * v2.x); }

template<typename T>
inline T dotProduct(const TVector3<T>& v1, const TVector3<T>& v2)
{ return v1.x * v2.x + v1.y * v2.y + v1.z * v2.z; }

template<typename T>
inline std::ostream& operator<<(std::ostream& output, const TVector3<T> &v)
{
    output << "[" <<  v.x << ", " << v.y << ", " << v.z << "]";
    return output;
}


typedef TVector2<float> Vector2f;
typedef TVector3<float> Vector3f;
typedef TVector3<int> Vector3i;


template<typename T>
struct TPoint2
{
    TPoint2() :
        x(0), y(0)
    {}

    TPoint2(T s) :
        x(s), y(s)
    {}

    TPoint2(T x, T y) :
        x(x), y(y)
    {}

    TPoint2(T vect[2]) :
        x(vect[0]), y(vect[1])
    {}

    T& operator[](int i)
    { return (&x)[i]; }

    T operator[](int i) const
    { return (&x)[i]; }

    TPoint2 operator+(const TVector2<T>& vec) const
    { return TPoint2(x+vec.x, y+vec.y); }

    TVector2<T> operator-(const TPoint2& p) const
    { return TVector2<T>(x-p.x, y-p.y); }

    TPoint2 operator-(const TVector2<T>& vec) const
    { return TPoint2(x-vec.x, y-vec.y); }

    inline TPoint2 operator-() const
    { return TPoint2(-x, -y); }

    T x, y;
};

template<typename T>
inline std::ostream& operator<<(std::ostream& output, const TPoint2<T> &v)
{
    output << "[" <<  v.x << ", " << v.y << "]";
    return output;
}


template<typename T>
struct TPoint3
{
    TPoint3() :
        x(0), y(0), z(0)
    {}

    TPoint3(T s) :
        x(s), y(s), z(s)
    {}

    TPoint3(T x, T y, T z) :
        x(x), y(y), z(z)
    {}

    TPoint3(T vect[3]) :
        x(vect[0]), y(vect[1]), z(vect[2])
    {}

    template<typename V3>
    explicit TPoint3(const V3& v) :
        x(v.x), y(v.y), z(v.z)
    {}

    T& operator[](int i)
    { return (&x)[i]; }

    T operator[](int i) const
    { return (&x)[i]; }

    TPoint3 operator+(const TVector3<T>& vec) const
    { return TPoint3(x+vec.x, y+vec.y, z+vec.z); }

    TVector3<T> operator-(const TPoint3& p) const
    { return TVector3<T>(x-p.x, y-p.y, z-p.z); }

    TPoint3 operator-(const TVector3<T>& vec) const
    { return TPoint3(x-vec.x, y-vec.y, z-vec.z); }


    T x, y, z;
};

template<typename T>
inline std::ostream& operator<<(std::ostream& output, const TPoint3<T> &v)
{
    output << "[" <<  v.x << ", " << v.y << ", " << v.z << "]";
    return output;
}


typedef TPoint2<float> Point2f;
typedef TPoint3<float> Point3f;
typedef TPoint2<int> Point2i;


#endif
